package net.jxtz.jxtzos.authentication;


import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import net.jxtz.jxtzos.entity.auth.User;
import net.jxtz.jxtzos.entity.web.R;
import net.jxtz.jxtzos.entity.web.UserInfoDto;
import net.jxtz.jxtzos.mapper.redis.RedisMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;

/**
 * jwt登陆认证处理类
 *
 * @author a123
 */
@Slf4j
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final AuthenticationManager authenticationManager;

    private final RedisMapper redisMapper;

    private String userName;

    @Value("${cross-path}")
    private String crossPath;

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager, RedisMapper redisMapper) {
        this.authenticationManager = authenticationManager;
        this.redisMapper = redisMapper;
        super.setFilterProcessesUrl("/authentication/login");
        super.setUsernameParameter("userName");
    }

    /**
     *
     * 拿到请求体中的user信息
     *
     * @param request request
     * @param response response
     * @return 校验对象
     * @throws AuthenticationException 校验异常
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        log.info("开始解析request中的User信息");
        User user = null;
        try {
            // 拿到通过requestBody传递的参数
            user = JSONObject.parseObject(request.getInputStream(), StandardCharsets.UTF_8, User.class);
            log.info("用户信息：" + user.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }

        if (user != null){
            this.userName = user.getUserName();
            return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword()));
        }else {
            String s= "用户信息不存在";
            log.error(s);
            try {
                unsuccessfulAuthentication(request,response, new AuthenticationServiceException(s));
            } catch (IOException | ServletException e) {
                e.printStackTrace();
            }
            return null;
        }
    }

    /**
     * 校验成功处理逻辑
     * 校验成功之后在response的头部放入access_token
     *
     * @param request request
     * @param response response
     * @param chain 过滤器对象
     * @param authResult 校验对象
     * @throws IOException IO异常
     * @throws ServletException servlet异常
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        String s = "身份校验成功";
        log.info(s);
        SecurityContextHolder.getContext().setAuthentication(authResult);
        final MyUserDetails user = (MyUserDetails)authResult.getPrincipal();
        response.setContentType("application/json;charset=utf-8");
        response.setHeader("Access-Control-Allow-Origin", crossPath);
        response.setHeader("Access-Control-Allow-Methods", "*");
        response.setHeader("Access-Control-Allow-Headers","*");
        response.setHeader("Access-Control-Expose-Headers", "*");
        response.setStatus(200);
        String token = JwtUtils.TOKEN_PREFIX + JwtUtils.create(user.getUsername(), user);
        // 放入access_token
        response.addHeader("access-token", token);
        redisMapper.set(user.getUsername() + JwtUtils.TOKEN_PARAM_PREFIX, token);
        PrintWriter writer = response.getWriter();
        UserInfoDto userInfoDto = new UserInfoDto();
        userInfoDto.setId(user.getId() + "");
        userInfoDto.setUserName(user.getUsername());
        userInfoDto.setNikeName(user.getNikeName());
        userInfoDto.setHeadUrl(user.getHeadUrl());
        userInfoDto.setAuthorityList(user.getAuthorities());
        writer.write(JSONObject.toJSONString(R.ok().message("登陆成功").data("user", userInfoDto)));
        writer.flush();
        writer.close();
    }

    /**
     * 校验失败处理逻辑
     *
     * @param request 请求体
     * @param response 响应体
     * @param failed 校验异常
     * @throws IOException io异常
     * @throws ServletException servlet异常
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        String s = "身份校验失败";
        log.info(s);
        failed.printStackTrace();
        redisMapper.deleteKey(userName + JwtUtils.TOKEN_PARAM_PREFIX);
        redisMapper.deleteKey(userName + MyCsrfTokenRepository.CSRF_PARAM);
    }
}
